/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/
#pragma once

class kMesh2DBase;
class kMesh2D;
class kEngine;
class kMaterial;

#include "krenderable.h"
#include "kimage.h"
#include "kblend.h"
#include "kemeshtype.h"

class kMesh2DBase : public kRenderable {
private:
	kEngine* _engine;
	const kMaterial* _material;
	mutable kAABB _aabb;
	kBlend _blend;
	mutable std::vector<kVec2> _transformedVertices;
	std::vector<kVec2> _vertices;
	std::vector<kVec2> _textureCoords;
	std::vector<kColor> _colors;
	keMeshType _meshType;
	bool _repeatX, _repeatY;

	void updateVertices() const;

protected:
	kMesh2DBase(kEngine* engine);
	inline void resize(size_t vertexCount, size_t textureStages, size_t colorStages) {
		_vertices.resize(vertexCount);
		_textureCoords.resize(vertexCount*textureStages);
		_colors.resize(vertexCount*colorStages);
		_hasTransformChanged = true;
	}
	inline void setTextureCoord(int vertex, const kVec2& uv, int stage = 0) {
		_textureCoords[stage*_vertices.size() + vertex] = uv;
	}
	inline void setColor(int vertex, const kColor& color, int stage = 0) {
		_colors[stage*_vertices.size() + vertex] = color;
	}
	inline void setVertex(int vertex, const kVec2& pos) {
		_vertices[vertex] = pos;
		_hasTransformChanged = true;
	}
	inline void setMeshType(keMeshType meshType) {
		_meshType = meshType;
	}

public:
	~kMesh2DBase();

	inline void setBlend(const kBlend& blend) {
		_blend = blend;
	}
	inline void setBlend(keBlendFactor src, keBlendOp op, keBlendFactor dst) {
		_blend = kBlend(src, op, dst);
	}
	inline void setBlend(keBlendFactor src, keBlendOp op, keBlendFactor dst, keBlendFactor srcAlpha, keBlendOp opAlpha, keBlendFactor dstAlpha) {
		_blend = kBlend(src, op, dst, srcAlpha, opAlpha, dstAlpha);
	}
	inline const kBlend& getBlend() const {
		return _blend;
	}
	inline void setMaterial(const kMaterial* material) {
		_material = material;
	}
	inline void setMaterialRepeat(bool repeatX, bool repeatY) {
		_repeatX = repeatX;
		_repeatY = repeatY;
	}
	inline const kMaterial* getMaterial() const {
		return _material;
	}
	inline bool getMaterialRepeatX() const {
		return _repeatX;
	}
	inline bool getMaterialRepeatY() const {
		return _repeatY;
	}
	inline const kVec2& getTextureCoord(int vertex, int stage = 0) const {
		return _textureCoords[stage*_vertices.size() + vertex];
	}
	inline const kColor& getColor(int vertex, int stage = 0) const {
		return _colors[stage*_vertices.size() + vertex];
	}
	inline const kVec2& getTransformedVertex(int vertex) const {
		if (_hasTransformChanged)
			updateVertices();

		return _transformedVertices[vertex];
	}
	inline const kVec2& getVertex(int vertex) const {
		return _vertices[vertex];
	}
	inline const kAABB& getAABB() const {
		if (_hasTransformChanged)
			updateVertices();

		return _aabb;
	}
	virtual void render(kGraphics* graphics);
	inline kEngine* getEngine() {
		return _engine;
	}
	inline size_t getVertexCount() {
		return _vertices.size();
	}
	inline size_t getTextureStages() {
		return _vertices.size() ? _textureCoords.size()/_vertices.size() : 0;
	}
	inline size_t getColorStages() {
		return _vertices.size() ? _colors.size()/_vertices.size() : 0;
	}
	inline keMeshType getMeshType() {
		return _meshType;
	}
	inline size_t getMappingCount() {
		switch (_meshType) {
		case keMesh_PointList:
		case keMesh_LineList:
		case keMesh_TriangleList:
			return getVertexCount();
		case keMesh_LineStrip:
			return (getVertexCount()-1)*2;
		case keMesh_LineLoop:
			return getVertexCount()*2;
		case keMesh_TriangleFan:
			return (getVertexCount()-2)*3;
		default:
			return 0;
		}
	}
	inline size_t getMapping(size_t index) {
		switch (_meshType) {
		case keMesh_PointList:
		case keMesh_LineList:
		case keMesh_TriangleList:
			return index;
		case keMesh_LineStrip:
			return (index+1)/2;
		case keMesh_LineLoop:
			return ((index+1)/2) % getVertexCount();
		case keMesh_TriangleFan:
			return (index % 3) ? (index+4)/3 : 0;
		default:
			return 0;
		}
	}
};

class kMesh2D : public kMesh2DBase {
public:
	inline kMesh2D(kEngine* engine) : kMesh2DBase(engine) {
	};
	inline void resize(size_t vertexCount, size_t textureStages, size_t colorStages) {
		kMesh2DBase::resize(vertexCount, textureStages, colorStages);
	}
	inline void setTextureCoord(int vertex, const kVec2& uv, int stage = 0) {
		kMesh2DBase::setTextureCoord(vertex, uv, stage);
	}
	inline void setColor(int vertex, const kColor& color, int stage = 0) {
		kMesh2DBase::setColor(vertex, color, stage);
	}
	inline void setVertex(int vertex, const kVec2& pos) {
		kMesh2DBase::setVertex(vertex, pos);
	}
	inline void setMeshType(keMeshType meshType) {
		kMesh2DBase::setMeshType(meshType);
	}
};
